<html>

<style>
    * {
        margin: 0;
    }

    #content {
        color: white;
        position: absolute;
        left: -20px;
        top: -10px;
        white-space: pre-wrap;
        height: 200px;
        overflow: auto;
    }
</style>

<script type="importmap">
	{
	  "imports": {
		"three": "../../three.js-r151/build/three.module.js",
		"three/addons/": "../../three.js-r151/examples/jsm/"
	  }
	}
</script>

<body>
    <div id="content">
        相机沿着管道漫游
    </div>
    <div id="canvas"></div>

    <script type="module">
        import * as THREE from 'three';
        import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

        // 1. 必须创建的三大件
        // 1.1 三维场景Scene, 类似创建了个世界，三维世界
        const scene = new THREE.Scene();

        // 创建管道方式一
        let path;
        {
            // 三维样条曲线
            path = new THREE.CatmullRomCurve3([
                new THREE.Vector3(-5, 2, 9),
                new THREE.Vector3(-1, 4, 4),
                new THREE.Vector3(0, 0, 0),
                new THREE.Vector3(6, -6, 0),
                new THREE.Vector3(9, -4, 6),
                new THREE.Vector3(12, 3, 3),
            ]);
            // 样条曲线path作为TubeGeometry参数生成管道
            const geometry = new THREE.TubeGeometry(path, 3, 5, 30);
            const texLoader = new THREE.TextureLoader();
            //纹理贴图
            const texture = texLoader.load('./diffuse.jpg');
            //UV坐标U方向阵列模式
            texture.wrapS = THREE.RepeatWrapping;
            //纹理沿着管道方向阵列(UV坐标U方向)
            texture.repeat.x = 2;
            const material = new THREE.MeshLambertMaterial({
                map: texture,
                side: THREE.DoubleSide, //双面显示看到管道内壁
            });
            const mesh = new THREE.Mesh(geometry, material);
            // scene.add(mesh);
        }

        // 创建管道方式二
        let path2
        {
            // https://threejs.org/docs/index.html?q=TubeGeometry#api/zh/geometries/TubeGeometry
            class CustomSinCurve extends THREE.Curve {
                constructor(scale = 1) {
                    super();
                    this.scale = scale;
                }

                getPoint(t, optionalTarget = new THREE.Vector3()) {
                    const tx = t * 3 - 1.5;
                    const ty = Math.sin(2 * Math.PI * t);
                    const tz = 0;
                    return optionalTarget.set(tx, ty, tz).multiplyScalar(this.scale);
                }
            }

            path2 = new CustomSinCurve(10);
            const geometry = new THREE.TubeGeometry(path2, 20, 2, 20, false);
            const texLoader = new THREE.TextureLoader();
            const texture = texLoader.load('./diffuse.jpg');
            const material = new THREE.MeshPhysicalMaterial({ 
                color: 0x00ff00, 
                map: texture,
                side: THREE.DoubleSide, 
                transparent: false,//开启透明
                opacity: 0.3,//设置透明度
            });
            const mesh = new THREE.Mesh(geometry, material);
            scene.add(mesh);
        }

        // 添加辅助坐标系
        const axesHelper = new THREE.AxesHelper(20);
        scene.add(axesHelper);

        // 1.2 相机
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        camera.position.set(20, 20, 20)

        // 1.3 渲染器
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        let canvasDom = document.getElementById("canvas");
        canvasDom.appendChild(renderer.domElement);


        // 2. 设置相机控件轨道控制器OrbitControls
        let controls = new OrbitControls(camera, canvasDom);

        // 3. 打光
        {
            const light = new THREE.DirectionalLight(0xFFFFFF);
            light.position.set(30, 30, 30);
            scene.add(light)

            const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444);
            hemiLight.position.set(0, 30, 0);
            scene.add(hemiLight);
        }

        // 4. 动画走起
        {

            // 从曲线上等间距获取一定数量点坐标
            const pointsArr = path2.getSpacedPoints(1000);
            // 渲染循环
            let i = 0; //在渲染循环中累加变化
            function render() {
                if (i < pointsArr.length - 1) {
                    // 相机位置设置在当前点位置
                    // 曲线上当前点pointsArr[i]和下一个点pointsArr[i+1]近似模拟当前点曲线切线
                    // 设置相机观察点为当前点的下一个点，相机视线和当前点曲线切线重合
                    camera.position.copy(pointsArr[i]);
                    camera.lookAt(pointsArr[i + 1]);
                    i += 1; //调节速度

                    // 相机控制没有用了？
                    // controls.target.copy(pointsArr[i + 1]);
                    // controls.update();
                } else {
                    i = 0
                }
                renderer.render(scene, camera);
                requestAnimationFrame(render);
            }
            render();

        }

    </script>
</body>

</html>